// Notice
//
// The contents of this file are subject to the
//  same license as the source code for ImageJ. FracLac is in the public domain.
// If you use it to publish research, please cite the source.
// The initial developer of the original
// code is Audrey Karperien, Charles Sturt University.
package fraclac.FLUtilities;

import java.text.DecimalFormat;
import java.util.Random;

/**Methods for manipulating numbers and arrays in FracLac plugin.
 *
 * @author  Audrey_Karperien
 */
public class FLutil {
    /**Commonly used string for tab character "\t".*/
    //private static String tab = "\t";
    
    /**Construction creates a new instance of util but takes
     * no parameters.*/
    public FLutil() {
    }
    /**************************************************************************/
    /**
     * When called after a fractal box count, seeks a representative covering
     * from a sample of coverings laid over a 2-dimensional pattern. The arrays
     * passed should be one array for each each location, each
     * holding at the position corresponding to
     * the position in the original list of box sizes, one value.  That value
     * is the number of covering objects at that &epsilon;.
     * The returned array holds the mean number of objects
     * needed to cover the pattern at each
     * &epsilon (covering box size), from all the scans.
     * For other applications, this is a float
     * array that is the average
     * value of each position
     * in the passed array of integer arrays.
     * <p>To finish the analysis,
     * pass the results to a
     * {@link FLAnalyzer.FracStats#FindDfStats fractal dimension} class.
     * </p>
     * <p>The passed integers
     * control how many arrays, and how many &epsilon;s in them,
     * are used to find the mean,
     * starting from index 0 in each array.  The number of &epsilon;s must
     * be the same for all arrays and
     * all the arrays must have at least as many members as the numBoxes
     * or an OutOfBounds Exception occurs. </p>
     *
     * @param   TestArray array of arrays of integers to be averaged
     *          at each position
     * @param   numBoxes the number of values in the arrays to use
     * @param   numLocs the number of arrays to assess
     * @return  a float array of the average at each position
     *          over all the arrays
     */
    public static float [] findAvgCover(int [][] TestArray,
            int numLocs, int numBoxes) {/*averages all arrays in a 2d array to make one value for each slot*/
        float  avcover[]= new float[numBoxes];
        //IJ.showMessage(fnum((double)TestArray[0][2]));
        /*initialize the array using the first location*/
        /*for each size, average boxes containing pixels from all locations*/
        /*store this in the array*/
        for (int a = 0; a < numBoxes; a++) {
            avcover[a] = 0.0f;}
        for (int j = 0; j<numLocs; j++) {
            for (int i = 0; i < numBoxes; i++) {
                avcover[i] =  avcover[i] + (float)TestArray[j][i];
            }
        }
        
        for (int i = 0; i < numBoxes; i++) {
            avcover[i] =  avcover[i]/(float)numLocs;
        }
        
        return avcover;
        
    }
    
    
    
    /*****************************************************************/
    /**
     * When called after a fractal box count scan over multiple origins,
     * seeks the set of coverings that most efficiently covered
     * an object in a plane.  Takes an array of arrays, one array at
     * each scan location holding
     * the number of coverings
     * used for each object size.  That is, the arrays
     * passed should be one array for each location, each
     * holding at the position corresponding to
     * the position in the original list of box sizes, one value.  That value
     * is the number of covering objects at that &epsilon;. Returns
     * a one-dimensional array holding one value for each &epsilon;,
     * equal to the minimum number, drawn from all the locations,
     * of covering objects at that &epsilon;.
     *
     * <p>To finish the analysis,
     * pass the results to a
     * {@link FLAnalyzer.FracStats#FindDfStats fractal dimension} class.
     * </p>
     *
     * <p>The passed integers
     * control how many sizes to use, starting from index 0, in each array,
     * but the number of &epsilon;s must be the same for all arrays and
     * all the arrays must have at least as many members as the numBoxes
     * or an OutOfBounds Exception occurs. </p>
     *
     *
     * @param   TestArray integer array of arrays, one for each location,
     *          that is as long as the number of sizes
     * @param   numLocs integer for the number of location array to use
     * @param   numBoxes integer for the number of counts from each location
     *          array to use
     * @return  mcover integer array holding the lowest value at each size
     */
    
    public static int [] findMinCover(int [][] TestArray, int numLocs,
            int numBoxes) {
        int  mcover[]= new int[numBoxes];
        
        /*initialize the minimum cover array using the first location*/
        /*for each size, find minimum boxes containing pixels
         from all locations*/
        /*store this in the mincover array*/
        for (int a = 0; a < numBoxes; a++) {
            mcover[a] = TestArray[0][a];}
        for (int j = 0; j<numLocs; j++) {
            for (int i = 0; i < numBoxes; i++) {
                if (TestArray[j][i]<mcover[i])
                    mcover[i] = TestArray[j][i];
            }
        }
        return mcover;
    }
    
  
    /** Removes areas of horizontal slope by retaining only whichever value
     *in an interval
     *of equal values had the smallest value.
     *
     *
     *
     * @param masses  is the array to assess (e.g., of counts
     * of boxes containing pixels in a box count)
     * @param N is the length of the array to consider
     * @param boxsizes is the integer array of box sizes
     * @return 2d array of masses plus the new box size array at the end
     */
    public static int [][] RemoveHorizontalSlopesAndMasses
            (int[] [] masses, int[]boxsizes, int N) {
        int [][]m = new int [N][];
        int []s = new int [N];
        s[0]=boxsizes[0];
        m[0]=masses[0];
        int d = 1;
        for (int q=1; q<N; q++)
            //for (int q=NumSizes-1;q>=0; q--)
        {
            boolean proceed = false;
            
            if (masses[q].length!=masses[q-1].length)proceed=true;
            //if(boxsizes[q]==0)System.out.println("eeek");
            if (proceed) {
                s[d]=boxsizes[q];
                m[d]=masses[q];
                d++;
            }
            
        }
        //int [] CSize=new int [d];
        int [][] NewMassesPlusSizesAtstart = new int [d+1][];
        NewMassesPlusSizesAtstart[0]=new int [d];
        for (int j=0; j < d; j++)NewMassesPlusSizesAtstart[0][j]=s[j];
        for (int i = 1; i < NewMassesPlusSizesAtstart.length; i++) {
            NewMassesPlusSizesAtstart[i]=m[i-1];
        }
        
        return NewMassesPlusSizesAtstart;
        
    }
    /**
     * Tests the functioning of the findMinCover and findAvgCover
     * methods.
     *
     * @param   args String array of arguments; is a dud
     *          because this class is only for testing
     */
    /*
    public static void main(String args[]) {
        int [][] A = {{4, 1, 2, 3, 5, 6, 3}, {5,4,2,1, 2, 3, 4}};
        int [][] D = {{1, 1, 1, 1, 1, 1, 1}, {4,4,4,4,4,4,4}};
        int []B = findMinCover(A, A.length, A[0].length);
        for (int i = 0; i< B.length; i++) System.out.println(B[i]);
        //;+ " " + B[1]+" "+ B[2]+ " "+ B[3]);
        float [] C = findAvgCover(D, D.length, D[0].length );
        for (int i = 0; i< C.length; i++) System.out.println(C[i]);
        //;+ " " + B[1]+" "+ B[2]+ " "+ B[3]);
        
    }
    */
    
    /**Utility creates and initializes an array using the passed parameters.
     *
     * @param   length int for the length of the new array
     * @param   value double for the value to initialize the array members to
     *
     * @return  double array of passed length, initialed to passed value
     */
    public static float[] newArray(int length, float value) {
        float [] Array=new float [length];
        for (int i =0; i < length; i++)
            Array[i]=value;
        return Array;
    }
    
    
//    public static float[] newArray(int length, float value)
//    { float [] Array=new float [length];
//      for (int i =0; i < length; i++)
//          Array[i]=value;
//      return Array;
//    }
    
    /**************************************************************************/
    /** Creates and initialized an array using the passed parameters.
     *
     * @param   length int for length of new array
     * @param   value int for value to initialize new array elements to
     * @return  int array of passed length, initialized to passed value
     */
    public static int[] newArray(int length, int value) {
        int [] Array=new int [length];
        for (int i =0; i < length; i++)
            Array[i]=value;
        return Array;
    }
    
    
    
    
    
    
    
    public static float[][] new2dArray(int length1, int length2, float value) {
        float [][] Array=new float [length1][length2];
        
        for (int i =0; i < length1; i++) {
            for (int j = 0; j<length2; j++)
                Array[i][j]=value;
        }
        return Array;
    }
    
    
    
    /**************************************************************************/
    /** Returns a new 2dimensional array initialized to
     * the passed value, and of the passed dimensions.
     *
     * @param   length1 int for length of first dimension
     * @param   length2 int for length of second dimension
     * @param   value int to initialize elements to
     * @return  int[][] initialized to passed value, of passed dimensions
     */
    public static int[][] new2dArray(int length1, int length2, int value) {
        int [][] Array=new int [length1][length2];
        
        for (int i =0; i < length1; i++) {
            for (int j = 0; j<length2; j++) Array[i][j]=value;
        }
        return Array;
    }
    
    
    
    
    public static String fnum(float sum) {
        
        DecimalFormat myFormatter = new DecimalFormat("####.####");
        
        String output = myFormatter.format(sum);
        return output;
    }
    
    /***************************************************************/
    /**Formats numbers as strings using the passed number of decimal
     * places.
     *
     * @param   sum number to format
     * @param   decimals number of decimal places
     * @return  String of formatted number */
    public static String fnum(double sum, int decimals) {
        
        DecimalFormat myFormatter = new DecimalFormat("####.####");
        switch (decimals) {
            case 0: myFormatter = new DecimalFormat("####.");
            case 1: myFormatter = new DecimalFormat("####.#");
            case 2: myFormatter = new DecimalFormat("####.##");
            case 3: myFormatter = new DecimalFormat("####.###");
            case 4: myFormatter = new DecimalFormat("####.####");
            case 5: myFormatter = new DecimalFormat("####.#####");
            case 6: myFormatter = new DecimalFormat("####.######");
            default: myFormatter = new DecimalFormat("####.##");
        }       String output = myFormatter.format(sum);
        return output;
    }
    
    
    /***************************************************************/
    /**Returns a double array holding the mean, standard deviation,
     * Coefficient of variation, and lacunarity statistic for the
     * passed probability distribution.
     * <ul><LI>The mean of the probability distribution
     * is: &#8721;(i*mprob[i])
     * where i is the mass value m[i], and mprob[i] is the weight</LI>
     * <LI>the std dev of the probability distribution is:
     * &#8730;&#8721;(((mprob[i]-meanProb)*(mprob[i]-meanProb))*i);</LI>
     * <LI>the Probability Density lacunarity
     * at this epsilon is:
     * (&#8721;M<sup>2</sup>-(&#8721;M*&#8721;M))/(&#8721;M*&#8721;M);
     * </LI><LI>where &#8721;M=&#8721;(i*mprob[i])
     * and &#8721;M<sup>2</sup> = &#8721;(i*i*mprob[i]);</LI></ul>
     *
     *
     * @param   mprob double array of arrays holding the value
     *          of the bin for the frequency distribution
     * at [0] and the corresponding
     *          probability at [1]
     * @param   LOC int specifying which location this scan is from
     * @return  double array of mean, stddev, cv, and lacunarity
     */
    public static float [] BinnedProbabilityStats(float [][] mprob, int LOC) {
        if (mprob==null)return(null);
        if (mprob.length==0)return(null);
        else {//bins at 0 and probs at 1
            //int numberOfBoxesWithPixels=boxes;
            float sumM = 0;
            float sumMsq = 0;
            // double sum = 0;
            for (int i = 0; i < mprob[0].length; i++) {//calculate moments as per chap. 8.4.10 , p. 238,  Innaccone
                
                //This is the summed densities of pixels
                sumM=sumM + (mprob[0][i]*mprob[1][i]);
                //This is the squared density
                sumMsq = sumMsq + (mprob[0][i]*mprob[0][i]*mprob[1][i]);
            }
            float meanProb=sumM;
            float variance=0;
            
            for (int i=0; i<mprob[0].length; i++) {
                if (mprob[1][i]>0) {
                    variance=variance
                            +(((mprob[0][i]-meanProb)*
                            (mprob[0][i]-meanProb))*mprob[1][i]);}
                
            }
            
            float[] stuff=new float[4];
            stuff[0]=meanProb;
            stuff[1]=(float)Math.sqrt(variance);
            stuff[2]=(float)Math.sqrt(variance)/meanProb;
            stuff[3]= (sumMsq-(sumM*sumM))/(sumM*sumM);
            
            return stuff;
        }
    }
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    public static float[] FloatArrayToLogInverseFloatArray(float[] A,
            int length) {
        float[]N=new float[length];
        for (int i = 0; i < length; i++)
            N[i]=(float)Math.log(1.0f/A[i]);
        return N;
    }
    
    
    
    
    
    /************************************************************/
    /**Makes a set of random x and y values that are
     * between 0 and x, and 0 and y using rnd.nextInt() function.
     * The array has one x,y pair for each origin that
     * the box count will use.
     *
     *@param    x integer for maximum x
     *@param    y integer for maximum y
     *@param    NumberOfGridPositions integer
     *@return   xy integer array of random coordinates
     */
    public static int[][] MakeSetOfRandomVariables(int x, int y,
            int NumberOfGridPositions) {
        int [][]xy = new int [NumberOfGridPositions][2];
        Random rnd = new Random();
        for (int i = 0; i < NumberOfGridPositions; i++) {//beware of negative values here
            
            xy[i][1]=rnd.nextInt(y-1);
            xy[i][0]=rnd.nextInt(x-1);
        }
        return xy;
    }
    
    
    
    
    
    
    /***********************************************************************/
    /**Returns a map that has keys and their corresponding mapped
     * values, from a passed integer array.  The array is sorted into bins and
     * the bins tallied.
     * ClassCastException, IllegalArgumentException,
     * NullPointerException, UnsupportedOperationException,
     * getting and putting values in a HashMap.
     *
     * @return      java.util.Map with a key for every integer that appeared in
     *              the list, and corresponding mapped members that
     *              are the number of times that integer
     *              appeared in the original array
     *
     * @param       args the list of arguments
     *
     */
    /*
    @SuppressWarnings({ "rawtypes", "unchecked" })
	private static Map LoadFrequencyTable(int[] args) {
        final Integer ONE = new Integer(1);
        Integer ARGS [] = new Integer[args.length];
        //           Convert the incoming array to an Integer array
        for (int i = 0; i < args.length ; i++)
            ARGS[i]=new Integer(args[i]);
        
        Map m = new HashMap();
        Integer freq=null;
        
        for (int i=0; i<args.length; i++) {
            try{
                freq = (Integer) m.get(ARGS[i]);//Map keys to values in the
                //                                          new map by
                //                                checking each value in the array
                //                                        for a value mapped to it
            }catch(UnsupportedOperationException n){} 
            catch(ClassCastException cc){} 
            catch(IllegalArgumentException g){} catch(NullPointerException hg){}
            try{m.put(ARGS[i],
                    (freq==null ? ONE : new Integer(freq.intValue() + 1)));
            //                     then if there is no value, making a new key
            //                     and giving it a value of 1 in the
            //                     returned map or else adding 1 to the
            //                     number counted so far and assigning that
            //                               new count as the value
            }catch(UnsupportedOperationException n){}
            catch(ClassCastException cc){} 
            catch(IllegalArgumentException g){} catch(NullPointerException hg){}
        }
        
        return m;
    }
    */
    
    
    
    /***********************************************************************/
    /**
     * Utility that returns a 2d integer array with keys at [0][i] and
     * their frequencies
     * at [1][i].
     * @return an int [][] that is a mapped list of the unique integers
     * that were in the passed array, where the array at index [0] is the
     * integers (masses) and the array at index [1]
     * is the corresponding frequency
     * of each integer (the frequency of the mass)
     * @param args the array of integers
     * @throws Exception
     */
   // private static int [][] GetFrequencyTable(int args[]) throws Exception {
   //     return ReadFrequencyTable(LoadFrequencyTable(args));
  //  }
    
    
    
    /*************************************************************************/
    /**Makes a frequency table from the passed Map, which must translate
     * into Integers.
     * @param m java.util.Map to assess
     * @return int [][] array of the keys at [0] and values at [1]
     */
    /*
    private static int[][] ReadFrequencyTable(Map m) {
        Integer [] KEYS = new Integer [m.size()];
        Integer [] VALUES = new Integer[m.size()];
        int c = 0;
        for (Iterator i=m.entrySet().iterator(); i.hasNext(); ) {
            Entry e = (Entry) i.next();
            KEYS[c]=(Integer)e.getKey();
            VALUES[c]=(Integer)e.getValue();
            c++;
        }
        
        int [] Keys = new int [c];
        int [] Values = new int [c];
        for (int i = 0; i < m.size(); i++) {
            Keys[i]=KEYS[i].intValue();
            Values[i]=VALUES[i].intValue();
            
        }
        return new int[][]
        {Keys, Values};
        
    }
    */
    /**************************************************************************/
    
    
    
    public static float[] IntToFloatArray(int[] array) {
        float [] N = new float[array.length];
        for (int i = 0; i<array.length; i++)
            N[i]=(float)array[i];
        return N;
    }
    
    
    /**************************************************************************/
    /**Returns an array whose elements are the original elements
     *divided by the sum of the array.
     * @param array an integer array to assess
     * @param NumElements and integer for the number of elements to assess
     * @return double array that contains each element divided by
     * the sum of the entire array, so that the sum of the new array
     * is 1*/
//    public static double[] MeanProbArray(int[] array, int NumElements)
//    {
//
//        double sum = SumArray(array, NumElements);
//        double [] N= new double [NumElements];
//        for (int i = 0; i < NumElements; i++)
//        {
//            N[i]=(((double)array[i])/sum);
//        }
//        return N;
//    }
    
    public static float[] MeanProbArray(int[] array, int NumElements) {
        
        float sum = (float)SumArray(array, NumElements);
        float [] N= new float [NumElements];
        for (int i = 0; i < NumElements; i++) {
            N[i]=(((float)array[i])/sum);
        }
        return N;
    }
    
    
    
    /**************************************************************************/
    /** Returns the sum of the elements in the passed array, up
     * to the number passed.
     * @return double for sum of all elements
     * @param Nums value for the number of values to add in the passed array
     * @param array integer array to sum
     */
    private static double SumArray(int[] array, int Nums) {
        double sum=0;
        for (int i =0; i < Nums; i++)sum=sum+(double)array[i];
        return sum;
    }
    public static int SumInts(int[] array, int Nums) {
        int sum=0;
        for (int i =0; i < Nums; i++)sum=sum+array[i];
        return sum;
    }
    /**************************************************************************/
    /** Returns the sum of the elements in the passed array, up
     * to the number passed.
     * @return double for sum of all elements
     * @param Nums int for the number of values to assess in the passed array
     * @param array integer array to sum
     */
    /*
    private static double SumArray(double[] array, int Nums) {
        double sum=0;
        for (int i =0; i < Nums; i++)sum=sum+ array[i];
        return sum;
    }
    */
    public static float SumArray(float[] array, int Nums) {
        float sum=0f;
        for (int i =0; i < Nums; i++)sum=sum+ array[i];
        return sum;
    }
    
    
    
    
    
}
